/*
 * Copyright 2015 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

/*
 * Copyright 2014 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.netty.handler.codec.http2;

import io.netty.handler.codec.UnsupportedValueConverter;
import io.netty.util.AsciiString;

import java.util.Arrays;
import java.util.List;

import static io.netty.handler.codec.http2.HpackUtil.equalsConstantTime;

final class HpackStaticTable
{

    // Appendix A: Static Table
    // http://tools.ietf.org/html/rfc7541#appendix-A
    private static final List<HpackHeaderField> STATIC_TABLE = Arrays.asList(
            /* 1 */ newEmptyHeaderField(":authority"),
            /* 2 */ newHeaderField(":method", "GET"),
            /* 3 */ newHeaderField(":method", "POST"),
            /* 4 */ newHeaderField(":path", "/"),
            /* 5 */ newHeaderField(":path", "/index.html"),
            /* 6 */ newHeaderField(":scheme", "http"),
            /* 7 */ newHeaderField(":scheme", "https"),
            /* 8 */ newHeaderField(":status", "200"),
            /* 9 */ newHeaderField(":status", "204"),
            /* 10 */ newHeaderField(":status", "206"),
            /* 11 */ newHeaderField(":status", "304"),
            /* 12 */ newHeaderField(":status", "400"),
            /* 13 */ newHeaderField(":status", "404"),
            /* 14 */ newHeaderField(":status", "500"),
            /* 15 */ newEmptyHeaderField("accept-charset"),
            /* 16 */ newHeaderField("accept-encoding", "gzip, deflate"),
            /* 17 */ newEmptyHeaderField("accept-language"),
            /* 18 */ newEmptyHeaderField("accept-ranges"),
            /* 19 */ newEmptyHeaderField("accept"),
            /* 20 */ newEmptyHeaderField("access-control-allow-origin"),
            /* 21 */ newEmptyHeaderField("age"),
            /* 22 */ newEmptyHeaderField("allow"),
            /* 23 */ newEmptyHeaderField("authorization"),
            /* 24 */ newEmptyHeaderField("cache-control"),
            /* 25 */ newEmptyHeaderField("content-disposition"),
            /* 26 */ newEmptyHeaderField("content-encoding"),
            /* 27 */ newEmptyHeaderField("content-language"),
            /* 28 */ newEmptyHeaderField("content-length"),
            /* 29 */ newEmptyHeaderField("content-location"),
            /* 30 */ newEmptyHeaderField("content-range"),
            /* 31 */ newEmptyHeaderField("content-type"),
            /* 32 */ newEmptyHeaderField("cookie"),
            /* 33 */ newEmptyHeaderField("date"),
            /* 34 */ newEmptyHeaderField("etag"),
            /* 35 */ newEmptyHeaderField("expect"),
            /* 36 */ newEmptyHeaderField("expires"),
            /* 37 */ newEmptyHeaderField("from"),
            /* 38 */ newEmptyHeaderField("host"),
            /* 39 */ newEmptyHeaderField("if-match"),
            /* 40 */ newEmptyHeaderField("if-modified-since"),
            /* 41 */ newEmptyHeaderField("if-none-match"),
            /* 42 */ newEmptyHeaderField("if-range"),
            /* 43 */ newEmptyHeaderField("if-unmodified-since"),
            /* 44 */ newEmptyHeaderField("last-modified"),
            /* 45 */ newEmptyHeaderField("link"),
            /* 46 */ newEmptyHeaderField("location"),
            /* 47 */ newEmptyHeaderField("max-forwards"),
            /* 48 */ newEmptyHeaderField("proxy-authenticate"),
            /* 49 */ newEmptyHeaderField("proxy-authorization"),
            /* 50 */ newEmptyHeaderField("range"),
            /* 51 */ newEmptyHeaderField("referer"),
            /* 52 */ newEmptyHeaderField("refresh"),
            /* 53 */ newEmptyHeaderField("retry-after"),
            /* 54 */ newEmptyHeaderField("server"),
            /* 55 */ newEmptyHeaderField("set-cookie"),
            /* 56 */ newEmptyHeaderField("strict-transport-security"),
            /* 57 */ newEmptyHeaderField("transfer-encoding"),
            /* 58 */ newEmptyHeaderField("user-agent"),
            /* 59 */ newEmptyHeaderField("vary"),
            /* 60 */ newEmptyHeaderField("via"),
            /* 61 */ newEmptyHeaderField("www-authenticate"));

    private static HpackHeaderField newEmptyHeaderField(CharSequence name)
    {
        return newHeaderField(name, AsciiString.EMPTY_STRING);
    }

    private static HpackHeaderField newHeaderField(CharSequence name,
            CharSequence value)
    {
        return new HpackHeaderField(AsciiString.of(name),
                AsciiString.of(value));
    }

    private static final CharSequenceMap<Integer> STATIC_INDEX_BY_NAME = createMap();

    /**
     * The number of header fields in the static table.
     */
    static final int length = STATIC_TABLE.size();

    /**
     * Return the header field at the given index value.
     */
    static HpackHeaderField getEntry(int index)
    {
        return STATIC_TABLE.get(index - 1);
    }

    /**
     * Returns the lowest index value for the given header field name in the
     * static table. Returns -1 if the header field name is not in the static
     * table.
     */
    static int getIndex(CharSequence name)
    {
        Integer index = STATIC_INDEX_BY_NAME.get(name);
        if (index == null)
        {
            return -1;
        }
        return index;
    }

    /**
     * Returns the index value for the given header field in the static table.
     * Returns -1 if the header field is not in the static table.
     */
    static int getIndex(CharSequence name, CharSequence value)
    {
        int index = getIndex(name);
        if (index == -1)
        {
            return -1;
        }

        // Note this assumes all entries for a given header field are
        // sequential.
        while (index <= length)
        {
            HpackHeaderField entry = getEntry(index);
            if (equalsConstantTime(name, entry.name) == 0)
            {
                break;
            }
            if (equalsConstantTime(value, entry.value) != 0)
            {
                return index;
            }
            index++;
        }

        return -1;
    }

    // create a map CharSequenceMap header name to index value to allow quick
    // lookup
    private static CharSequenceMap<Integer> createMap()
    {
        int length = STATIC_TABLE.size();
        @SuppressWarnings("unchecked")
        CharSequenceMap<Integer> ret = new CharSequenceMap<Integer>(true,
                UnsupportedValueConverter.<Integer> instance(), length);
        // Iterate through the static table in reverse order to
        // save the smallest index for a given name in the map.
        for (int index = length; index > 0; index--)
        {
            HpackHeaderField entry = getEntry(index);
            CharSequence name = entry.name;
            ret.set(name, index);
        }
        return ret;
    }

    // singleton
    private HpackStaticTable()
    {
    }
}
